package com.yuyou.fn.settlement.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.github.wens.framework.service.impl.SuperServiceImpl;
import com.github.wens.mq.RedisMessageQueue;
import com.github.wens.mybatisplus.examples.Example;
import com.github.wens.mybatisplus.toolkit.IdWorker;
import com.pingplusplus.Pingpp;
import com.pingplusplus.model.Charge;
import com.pingplusplus.model.Event;
import com.pingplusplus.model.Webhooks;
import com.yuyou.fn.common.constant.Constants;
import com.yuyou.fn.common.exception.BusException;
import com.yuyou.fn.common.util.JSONS;
import com.yuyou.fn.common.util.TimeUtils;
import com.yuyou.fn.common.util.Utils;
import com.yuyou.fn.common.service.ISiteConfigService;
import com.yuyou.fn.settlement.entity.PayOrder;
import com.yuyou.fn.settlement.mapper.PayOrderMapper;
import com.yuyou.fn.settlement.service.IPayOrderService;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.security.*;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

/**
 *
 * PayOrder 表数据服务层接口实现类
 *
 */
@Service
public class PayOrderServiceImpl extends SuperServiceImpl<PayOrderMapper, PayOrder,Long> implements IPayOrderService ,InitializingBean {

    private static final Logger log = LoggerFactory.getLogger(PayOrderServiceImpl.class) ;

    @Resource
    private ISiteConfigService siteConfigService ;

    @Resource
    private RedisMessageQueue redisMessageQueue ;

    private  String secretKey ;

    private String appId;


    private PublicKey pingxxPublicKey  ;

    @Value("${dev.env}")
    private boolean dev;



    @Override
    public void afterPropertiesSet() throws Exception {
        //dev = false ;
        secretKey = dev ? siteConfigService.getConfigValueFromCache("pingxx_test_secret_key") : siteConfigService.getConfigValueFromCache("pingxx_live_secret_key");
        appId = siteConfigService.getConfigValueFromCache("pingxx_app_id");
        String pubKeyString  = siteConfigService.getConfigValueFromCache("pingxx_public_key");
        pubKeyString = pubKeyString.replaceAll("(-+BEGIN PUBLIC KEY-+\\r?\\n|-+END PUBLIC KEY-+\\r?\\n?)", "");
        byte[] keyBytes = Base64.decodeBase64(pubKeyString);

        // generate public key
        X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        this.pingxxPublicKey = keyFactory.generatePublic(spec);
    }

    @Override
    public Charge preparePay(Long busId ,String busType , BigDecimal amount , String channel , String subject , String body ,Map<String,String> extra ) {


        PayOrder payOrder = new PayOrder();
        Long orderId  = IdWorker.getId() ;



        Pingpp.apiKey = secretKey ;

        Map<String, Object> chargeParams = new HashMap<>();
        chargeParams.put("order_no",  orderId );
        chargeParams.put("amount", Utils.formatBigDecimal(amount.multiply(new BigDecimal("100")),"0"));//订单总金额, 人民币单位：分（如订单总金额为 1 元，此处请填 100）
        Map<String, String> app = new HashMap<String, String>();
        app.put("id", appId );
        chargeParams.put("app", app);
        chargeParams.put("channel",  channel );
        chargeParams.put("currency", "cny");
        chargeParams.put("client_ip",  "127.0.0.1");
        chargeParams.put("subject",  subject );
        if(StringUtils.isNotEmpty(body)){
            chargeParams.put("body",   body );
        }

        if(extra != null ){
            chargeParams.put("extra" , extra ) ;
        }

        Charge charge = null ;

        try {
            charge = Charge.create(chargeParams);
            log.info("Create charge result:{}" , charge );

        } catch (Exception e) {
            log.error("Create charge fail:{}" , e );
            throw new BusException("Create order fail" ,e );
        }

        payOrder.setPayOrderId(orderId);
        payOrder.setAmount(amount);
        payOrder.setBusId(busId);
        payOrder.setBusType(busType);
        payOrder.setCreateTime(TimeUtils.now());
        payOrder.setUpdateTime(payOrder.getCreateTime());
        payOrder.setChannel(channel);
        payOrder.setStatus(Constants.PAY_ORDER_STATUS_OF_NOT_PAY);
        payOrder.setSubject(subject);
        payOrder.setBody(body);
        payOrder.setChargeId(charge.getId());
        insertSelective(payOrder);

        return charge ;

    }

    @Override
    public void callback(String returnContent, String signature) {
        try {
            boolean r  = verifyData(returnContent,signature,pingxxPublicKey);
            if(!r){
                throw new IllegalArgumentException("Invalid data" );
            }
        } catch (Exception e) {
            log.error("Verify data fail:{}",e );
            throw new BusException(e);
        }

        Event event = Webhooks.eventParse(returnContent);
        if ("charge.succeeded".equals(event.getType())) {
            Charge charge = (Charge) event.getData().getObject();
            String chargeId = charge.getId();
            String orderNo = charge.getOrderNo();
            updateOrderStatus(chargeId , Long.valueOf( orderNo ), Constants.PAY_ORDER_STATUS_OF_PAID );
        } else if ("refund.succeeded".equals(event.getType())) {

        } else {
        }

    }

    @Transactional
    public void updateOrderStatus(String chargeId  , Long orderId  ,Integer status ) {
        PayOrder order = findById(orderId);
        if(order.getStatus() == status ){
            return;
        }

        PayOrder payOrder = new PayOrder();
        payOrder.setStatus(status);
        Example<PayOrder> example = PayOrder.createExample();
        example.createCriteria().andEqualTo("payOrderId" , orderId ).andEqualTo("chargeId" ,chargeId );
        boolean s = updateSelectiveByExample(payOrder, example );

        if(s && status == Constants.PAY_ORDER_STATUS_OF_PAID ){
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("payOrderId" , payOrder );
            jsonObject.put("busId",order.getBusId());
            jsonObject.put("busType",order.getBusType());
            jsonObject.put("amount" ,order.getAmount());
            redisMessageQueue.publish("order:pay:success:" + order.getBusType() , JSONS.toBytes( jsonObject ) );
        }
    }


    private  boolean verifyData(String dataString, String signatureString, PublicKey publicKey)
            throws NoSuchAlgorithmException, InvalidKeyException, SignatureException, UnsupportedEncodingException {

        byte[] signatureBytes = Base64.decodeBase64(signatureString);
        Signature signature = Signature.getInstance("SHA256withRSA");
        signature.initVerify(publicKey);
        signature.update(dataString.getBytes("UTF-8"));
        return signature.verify(signatureBytes);
    }

    @Override
    public PayOrder findByPayOrderIdAndChannel(Long busId,String channel) {
        Example<PayOrder> example=PayOrder.createExample();
        Example.Criteria criteria=example.createCriteria();
        criteria.andEqualTo("busId",busId);
        criteria.andEqualTo("channel",channel);
        return this.findOneByExample(example);
    }
}